<template>
    <section>
        <el-button type="primary" @click="handleInsert('insertRoot',null,{})" text plaint>添加根节点</el-button>
        <!-- 
            lazy：使用懒加载，异步加载子节点
            draggable：允许拖动节点
        -->
        <el-tree 
        :key="courseId"
        :props="defaultProps" 
        :data="tableData" 
        :load="loadNode" 
        :allowDrag="allowDrag" 
        :allowDrop="allowDrop"
        ref="elTreeRef"
            @node-drop="handleNodeDrop" lazy draggable>
            <template #default="{ node,data }">
                <div class="tree-node">
                    <el-icon>
                        <Folder v-if="data.type == 1"></Folder>
                        <VideoCamera v-else-if="data.type == 2"></VideoCamera>
                        <Document v-else></Document>
                    </el-icon>
                    <span>{{ data.name }}</span>
                    <div class="btn-box">
                        <el-icon v-if="node.level == 1"
                            class="insert-icon"
                            @click.stop="handleInsert('insert',node,data)">
                            <Plus />
                        </el-icon>
                        <el-icon 
                            class="update-icon"
                            @click.stop="handleUpdate(node,data)">
                            <Edit />
                        </el-icon>
                        <el-icon 
                            class="delete-icon"
                            @click.stop="handleDelete(node,data)">
                            <Delete />
                        </el-icon>
                    </div>
                </div>
            </template>
        </el-tree>
    </section>
</template>
<script lang="ts" setup>
// 引入页面需要用到的图标
import {
    Document,
    VideoCamera,
    Folder,
    Delete,
    Plus,
    Edit,
} from '@element-plus/icons-vue'
import { defineProps, reactive, defineEmits,ref,defineExpose } from "vue";
import request from "@/utils/request";
import type Node from 'element-plus/es/components/tree/src/model/node'
import {ElMessageBox} from "element-plus";


/**
 * 树状菜单数据模型
 */
interface Tree {
    id: number,
    course_id: number,
    parent_id?: number,
    name: string,
    type: number,
    leaf: boolean
}


// 树状菜单项的属性
const defaultProps = {
    children: 'children',
    label: 'name',
    isLeaf: 'leaf'
}

// 树状菜单显示的数据
const tableData = reactive<any>([]);

const props = defineProps({
    courseId: {
        type: Number,
        required: true
    }
});

const emits = defineEmits(["show"]);

// 树状菜单的ref
const elTreeRef = ref<any>();

/**
 * 记录点击了的节点
 */
let clickNode:Node;

/**
 * 添加节点：让父组件显示增加组件
 */
const handleInsert = (showValue:string,node: Node | null, data: any) => {
    if (node) {
        clickNode = node;
    }
    // 计算排序字段：如果点击“增加根节点”，那么根据根节点的子节点数量+1
    if (null == node) {
        data.sort = elTreeRef.value.root.childNodes.length + 1;
    } else {
        data.sort = node.childNodes.length + 1;
    }
    emits("show", showValue, data);
}

/**
 * 修改节点：让父组件显示修改组件
 */
const handleUpdate = (node:Node,data: Tree) => {
    clickNode = node;
    const showValue = node.level == 1 ? 'updateRoot' : "update";
    emits("show", showValue, data);
}

/**
 * 展开节点
 * @param node 
 */
const expand = (node:Node) => {
    return new Promise((resolve) => {
        node.expand(() => {
            resolve(true);
        });
    });
}

/**
 * 删除节点
 */
 const handleDelete = async (node:Node,data: Tree) => {
    if (node.level == 1 && !node.expanded) {
        // 如果节点是根节点，需要先展开节点，避免存在子节点的情况下进行删除
       await expand(node);
    }

    // 判断是否有子节点，存在子节点时不能删除
    if (node.childNodes.length > 1) {
        alert("无法删除有子节点的节点");
        return;
    }
    
    ElMessageBox.confirm("确认删除该选项吗，删除后将不可恢复?", "删除确认", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    }).then(() => {
        request({url: `/chapter/delete/${data.id}`,method:"DELETE"}).then(
            ({ data }) => {
                if (data.code > 0) {
                    alert("删除成功");
                    // 从树形组件中删除
                    elTreeRef.value.remove(node);
                } else {
                    alert("删除失败");
                }
            }
        );
    });
    emits("show", { update: true }, data);
}

/**
 * 懒加载叶子结点的方法
 */
const loadNode = (node: Node, resolve: (data: Tree[]) => void) => {
    const data: any = {};
    const level = node.level;
    if (level == 0) {
        data.course_id = props.courseId;
    } else {
        data.parent_id = node.data.id;
    }
    console.log(data)
    request({
        url: "/chapter/queryList",
        data: data,
        method: "post"
    }).then((res: any) => {
        // 查询成功
        if (res.data.code > 0) {
            res.data.data.list.forEach((item: any) => {
                item.leaf = level == 1;
            })
            resolve(res.data.data.list);
        }

    })
}

/**
 * 所有节点都允许拖动
 */
const allowDrag = () => true;

/**
 * 判断是否能够放下拖动
 * @param draggingNode 
 * @param dropNode 
 * @param type 
 */
const allowDrop = (draggingNode: Node, dropNode: Node, type: string) => {
    // 只能拖动到前面或后面，不能拖动到内部
    if (type == "inner") {
        return false;
    }
    // 节点层级需要一样
    if (draggingNode.level == dropNode.level) {
        // 第一层允许
        if (draggingNode.level == 1) {
            return true;
        }
        // 节点不一样的时候，父节点要一样才能拖动
        return draggingNode.data.parent_id == dropNode.data.parent_id;
    }
    return false;
}

let timeoutId: number;

/**
 * 拖动节点完成：通过父节点找到所有子节点，更新排序
 */
const handleNodeDrop = (draggingNode: Node,
    dropNode: Node) => {
    const parent = dropNode.parent;
    const ids = parent.childNodes.map(v => v.data.id);

    if (timeoutId) {
        clearTimeout(timeoutId);
    }

    // 做个节流处理，防止频繁请求
    timeoutId = setTimeout(() => {
        request({
            url: "/chapter/updateSort",
            data: { ids },
            method: "post"
        }).then(res => {
            if (res.data.code > 0) {
                alert("排序更新成功");
            }
        })
    }, 3000);
};

// 添加节点
const appendNode = (data:Tree) => {
    // 如果clickNode为null，那么添加根节点
    elTreeRef.value.append(data,clickNode);
};

// 更新节点的数据
const updateNode = (data:Tree) => {
   clickNode.data = data;
};

// 将添加节点的方法暴露给父组件
defineExpose({
    appendNode,updateNode
});
</script>
<style lang="scss" scoped>
.tree-node {
    display: flex;
    align-items: center;

    .el-icon {
        margin-right: 5px;
    }

    .btn-box {

        display: flex;
        align-items: center;
        padding-left: 5px;

        .insert-icon {
            color: var(--el-color-success)
        }

        .update-icon {
            color: var(--el-color-primary)
        }

        .delete-icon {
            color: var(--el-color-danger)
        }

    }


}
</style>